91463557010d1e2f84a0c306cc368ed5ec951047
[idea/community.git] / plugins / git4idea / tests / git4idea / log / GitLogProviderTest.java
1 /*
2  * Copyright 2000-2014 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package git4idea.log;
17
18 import com.intellij.openapi.components.ServiceManager;
19 import com.intellij.openapi.util.Condition;
20 import com.intellij.openapi.util.text.StringUtil;
21 import com.intellij.openapi.vcs.VcsException;
22 import com.intellij.openapi.vfs.VirtualFile;
23 import com.intellij.util.ArrayUtil;
24 import com.intellij.util.CollectConsumer;
25 import com.intellij.util.Consumer;
26 import com.intellij.util.Function;
27 import com.intellij.util.containers.ContainerUtil;
28 import com.intellij.vcs.log.*;
29 import com.intellij.vcs.log.data.VcsLogBranchFilterImpl;
30 import com.intellij.vcs.log.impl.*;
31 import com.intellij.vcs.log.impl.VcsLogUserFilterImpl;
32 import com.intellij.vcsUtil.VcsFileUtil;
33 import git4idea.test.GitSingleRepoTest;
34 import git4idea.test.GitTestUtil;
35 import org.jetbrains.annotations.NotNull;
36 import org.jetbrains.annotations.Nullable;
37
38 import java.io.IOException;
39 import java.util.Collections;
40 import java.util.List;
41 import java.util.Set;
42
43 import static git4idea.test.GitExecutor.*;
44 import static java.util.Collections.singleton;
45
46 public class GitLogProviderTest extends GitSingleRepoTest {
47
48   private GitLogProvider myLogProvider;
49   private VcsLogObjectsFactory myObjectsFactory;
50
51   public void setUp() throws Exception {
52     super.setUp();
53     myLogProvider = GitTestUtil.findGitLogProvider(myProject);
54     myObjectsFactory = ServiceManager.getService(myProject, VcsLogObjectsFactory.class);
55   }
56
57   public void tearDown() throws Exception {
58     super.tearDown();
59   }
60
61   public void test_init_with_tagged_branch() throws VcsException {
62     prepareSomeHistory();
63     List<VcsCommitMetadata> expectedLogWithoutTaggedBranch = log();
64     createTaggedBranch();
65
66     VcsLogProvider.DetailedLogData block = myLogProvider.readFirstBlock(myProjectRoot,new RequirementsImpl(1000, false, Collections.<VcsRef>emptySet()));
67     assertOrderedEquals(block.getCommits(), expectedLogWithoutTaggedBranch);
68   }
69
70   public void test_refresh_with_new_tagged_branch() throws VcsException {
71     prepareSomeHistory();
72     Set<VcsRef> prevRefs = GitTestUtil.readAllRefs(myProjectRoot, myObjectsFactory);
73     createTaggedBranch();
74
75     List<VcsCommitMetadata> expectedLog = log();
76     VcsLogProvider.DetailedLogData block = myLogProvider.readFirstBlock(myProjectRoot, new RequirementsImpl(1000, true, prevRefs));
77     assertSameElements(block.getCommits(), expectedLog);
78   }
79
80   public void test_refresh_when_new_tag_moved() throws VcsException {
81     prepareSomeHistory();
82     Set<VcsRef> prevRefs = GitTestUtil.readAllRefs(myProjectRoot, myObjectsFactory);
83     git("tag -f ATAG");
84
85     List<VcsCommitMetadata> expectedLog = log();
86     Set<VcsRef> refs = GitTestUtil.readAllRefs(myProjectRoot, myObjectsFactory);
87     VcsLogProvider.DetailedLogData block = myLogProvider.readFirstBlock(myProjectRoot, new RequirementsImpl(1000, true, prevRefs));
88     assertSameElements(block.getCommits(), expectedLog);
89     assertSameElements(block.getRefs(), refs);
90   }
91
92   public void test_new_tag_on_old_commit() throws VcsException {
93     prepareSomeHistory();
94     Set<VcsRef> prevRefs = GitTestUtil.readAllRefs(myProjectRoot, myObjectsFactory);
95     List<VcsCommitMetadata> log = log();
96     String firstCommit = log.get(log.size() - 1).getId().asString();
97     git("tag NEW_TAG " + firstCommit);
98
99     Set<VcsRef> refs = GitTestUtil.readAllRefs(myProjectRoot, myObjectsFactory);
100     VcsLogProvider.DetailedLogData block = myLogProvider.readFirstBlock(myProjectRoot, new RequirementsImpl(1000, true, prevRefs));
101     assertSameElements(block.getRefs(), refs);
102   }
103
104   public void test_all_log_with_tagged_branch() throws VcsException {
105     prepareSomeHistory();
106     createTaggedBranch();
107     List<VcsCommitMetadata> expectedLog = log();
108     List<TimedVcsCommit> collector = ContainerUtil.newArrayList();
109     //noinspection unchecked
110     myLogProvider.readAllHashes(myProjectRoot, new CollectConsumer<>(collector));
111     assertOrderedEquals(expectedLog, collector);
112   }
113
114   public void test_get_current_user() throws Exception {
115     VcsUser user = myLogProvider.getCurrentUser(myProjectRoot);
116     assertNotNull("User is not defined", user);
117     VcsUser expected = getDefaultUser();
118     assertEquals("User name is incorrect", expected.getName(), user.getName());
119     assertEquals("User email is incorrect", expected.getEmail(), user.getEmail());
120   }
121
122   public void test_dont_report_origin_HEAD() throws Exception {
123     prepareSomeHistory();
124     git("update-ref refs/remotes/origin/HEAD master");
125
126     VcsLogProvider.DetailedLogData block = myLogProvider.readFirstBlock(myProjectRoot,
127                                                                         new RequirementsImpl(1000, false, Collections.<VcsRef>emptySet()));
128     assertFalse("origin/HEAD should be ignored", ContainerUtil.exists(block.getRefs(), new Condition<VcsRef>() {
129       @Override
130       public boolean value(VcsRef ref) {
131         return ref.getName().equals("origin/HEAD");
132       }
133     }));
134   }
135
136   public void test_support_equally_named_branch_and_tag() throws Exception {
137     prepareSomeHistory();
138     git("branch build");
139     git("tag build");
140
141     VcsLogProvider.DetailedLogData data = myLogProvider.readFirstBlock(myProjectRoot,
142                                                                        new RequirementsImpl(1000, true, Collections.<VcsRef>emptySet()));
143     List<VcsCommitMetadata> expectedLog = log();
144     assertOrderedEquals(data.getCommits(), expectedLog);
145     assertTrue(ContainerUtil.exists(data.getRefs(), new Condition<VcsRef>() {
146       @Override
147       public boolean value(VcsRef ref) {
148         return ref.getName().equals("build") && ref.getType() == GitRefManager.LOCAL_BRANCH;
149       }
150     }));
151     assertTrue(ContainerUtil.exists(data.getRefs(), new Condition<VcsRef>() {
152       @Override
153       public boolean value(VcsRef ref) {
154         return ref.getName().equals("build") && ref.getType() == GitRefManager.TAG;
155       }
156     }));
157   }
158
159   public void test_filter_by_branch() throws Exception {
160     List<String> hashes = generateHistoryForFilters(true);
161     VcsLogBranchFilter branchFilter = VcsLogBranchFilterImpl.fromBranch("feature");
162     List<String> actualHashes = getFilteredHashes(branchFilter, null);
163     assertEquals(hashes, actualHashes);
164   }
165
166   public void test_filter_by_branch_and_user() throws Exception {
167     List<String> hashes = generateHistoryForFilters(false);
168     VcsLogBranchFilter branchFilter = VcsLogBranchFilterImpl.fromBranch("feature");
169     VcsLogUserFilter userFilter = new VcsLogUserFilterImpl(singleton(GitTestUtil.USER_NAME), Collections.<VirtualFile, VcsUser>emptyMap(),
170                                                            Collections.<VcsUser>emptySet());
171     List<String> actualHashes = getFilteredHashes(branchFilter, userFilter);
172     assertEquals(hashes, actualHashes);
173   }
174
175   public void test_short_details() throws Exception {
176     prepareLongHistory(VcsFileUtil.FILE_PATH_LIMIT * 2 / 40);
177     List<VcsCommitMetadata> log = log();
178
179     final List<String> hashes = ContainerUtil.newArrayList();
180     myLogProvider.readAllHashes(myProjectRoot, new Consumer<TimedVcsCommit>() {
181       @Override
182       public void consume(TimedVcsCommit timedVcsCommit) {
183         hashes.add(timedVcsCommit.getId().asString());
184       }
185     });
186
187     List<? extends VcsShortCommitDetails> shortDetails = myLogProvider.readShortDetails(myProjectRoot, hashes);
188
189     Function<VcsShortCommitDetails, String> shortDetailsToString = getShortDetailsToString();
190     assertOrderedEquals(ContainerUtil.map(shortDetails, shortDetailsToString), ContainerUtil.map(log, shortDetailsToString));
191   }
192
193   public void test_full_details() throws Exception {
194     prepareLongHistory(VcsFileUtil.FILE_PATH_LIMIT * 2 / 40);
195     List<VcsCommitMetadata> log = log();
196
197     final List<String> hashes = ContainerUtil.newArrayList();
198     myLogProvider.readAllHashes(myProjectRoot, new Consumer<TimedVcsCommit>() {
199       @Override
200       public void consume(TimedVcsCommit timedVcsCommit) {
201         hashes.add(timedVcsCommit.getId().asString());
202       }
203     });
204
205     List<? extends VcsFullCommitDetails> fullDetails = myLogProvider.readFullDetails(myProjectRoot, hashes);
206
207     // we do not check for changes here
208     final Function<VcsShortCommitDetails, String> shortDetailsToString = getShortDetailsToString();
209     Function<VcsCommitMetadata, String> metadataToString = new Function<VcsCommitMetadata, String>() {
210       @Override
211       public String fun(VcsCommitMetadata details) {
212         return shortDetailsToString.fun(details) + "\n" + details.getFullMessage();
213       }
214     };
215     assertOrderedEquals(ContainerUtil.map(fullDetails, metadataToString), ContainerUtil.map(log, metadataToString));
216   }
217
218   @NotNull
219   private Function<VcsShortCommitDetails, String> getShortDetailsToString() {
220     return new Function<VcsShortCommitDetails, String>() {
221       @Override
222       public String fun(VcsShortCommitDetails details) {
223         String result = "";
224
225         result += details.getId().toShortString() + "\n";
226         result += details.getAuthorTime() + "\n";
227         result += details.getAuthor() + "\n";
228         result += details.getCommitTime() + "\n";
229         result += details.getCommitter() + "\n";
230         result += details.getSubject();
231
232         return result;
233       }
234     };
235   }
236
237   /**
238    * Generates some history with two branches: master and feature, and made by two users.
239    * Returns hashes of this history filtered by the given parameters:
240    * @param takeAllUsers     if true, don't filter by users, otherwise filter by default user.
241    */
242   private List<String> generateHistoryForFilters(boolean takeAllUsers) {
243     List<String> hashes = ContainerUtil.newArrayList();
244     hashes.add(last());
245
246     GitTestUtil.setupUsername("bob.smith", "bob.smith@example.com");
247     if (takeAllUsers) {
248       String commitByBob = tac("file.txt");
249       hashes.add(commitByBob);
250     }
251     GitTestUtil.setupDefaultUsername();
252
253     hashes.add(tac("file1.txt"));
254     git("checkout -b feature");
255     String commitOnlyInFeature = tac("file2.txt");
256     hashes.add(commitOnlyInFeature);
257     git("checkout master");
258     String commitOnlyInMaster = tac("master.txt");
259
260     Collections.reverse(hashes);
261     refresh();
262     return hashes;
263   }
264
265   @NotNull
266   private List<String> getFilteredHashes(@Nullable VcsLogBranchFilter branchFilter,
267                                          @Nullable VcsLogUserFilter userFilter) throws VcsException {
268     VcsLogFilterCollectionImpl filters = new VcsLogFilterCollectionImpl(branchFilter, userFilter, null, null, null, null, null);
269     List<TimedVcsCommit> commits = myLogProvider.getCommitsMatchingFilter(myProjectRoot, filters, -1);
270     return ContainerUtil.map(commits, new Function<TimedVcsCommit, String>() {
271       @Override
272       public String fun(TimedVcsCommit commit) {
273         return commit.getId().asString();
274       }
275     });
276   }
277
278   private static void prepareSomeHistory() {
279     tac("a.txt");
280     git("tag ATAG");
281     tac("b.txt");
282   }
283
284   private static void prepareLongHistory(int size) throws IOException {
285     for (int i = 0; i < size; i++) {
286       String file = "a" + (i % 10) + ".txt";
287       if (i < 10) {
288         tac(file);
289       }
290       else {
291         modify(file);
292       }
293     }
294   }
295
296   private static void createTaggedBranch() {
297     String hash = last();
298     tac("c.txt");
299     tac("d.txt");
300     tac("e.txt");
301     git("tag poor-tag");
302     git("reset --hard " + hash);
303   }
304
305   @NotNull
306   private static VcsUser getDefaultUser() {
307     return new VcsUserImpl(GitTestUtil.USER_NAME, GitTestUtil.USER_EMAIL);
308   }
309
310   @NotNull
311   private List<VcsCommitMetadata> log() {
312     String output = git("log --all --date-order --full-history --sparse --pretty='%H|%P|%ct|%s|%B'");
313     final VcsUser defaultUser = getDefaultUser();
314     final Function<String, Hash> TO_HASH = new Function<String, Hash>() {
315       @Override
316       public Hash fun(String s) {
317         return HashImpl.build(s);
318       }
319     };
320     return ContainerUtil.map(StringUtil.splitByLines(output), new Function<String, VcsCommitMetadata>() {
321       @Override
322       public VcsCommitMetadata fun(String record) {
323         String[] items = ArrayUtil.toStringArray(StringUtil.split(record, "|", true, false));
324         long time = Long.valueOf(items[2]) * 1000;
325         return new VcsCommitMetadataImpl(TO_HASH.fun(items[0]), ContainerUtil.map(items[1].split(" "), TO_HASH), time,
326                                          myProjectRoot, items[3], defaultUser, items[4], defaultUser, time);
327       }
328     });
329   }
330 }