03190ab7c3a8c91bcf5f6fdadb3cd0312518b6af
[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(), ref -> ref.getName().equals("origin/HEAD")));
129   }
130
131   public void test_support_equally_named_branch_and_tag() throws Exception {
132     prepareSomeHistory();
133     git("branch build");
134     git("tag build");
135
136     VcsLogProvider.DetailedLogData data = myLogProvider.readFirstBlock(myProjectRoot,
137                                                                        new RequirementsImpl(1000, true, Collections.<VcsRef>emptySet()));
138     List<VcsCommitMetadata> expectedLog = log();
139     assertOrderedEquals(data.getCommits(), expectedLog);
140     assertTrue(ContainerUtil.exists(data.getRefs(), ref -> ref.getName().equals("build") && ref.getType() == GitRefManager.LOCAL_BRANCH));
141     assertTrue(ContainerUtil.exists(data.getRefs(), ref -> ref.getName().equals("build") && ref.getType() == GitRefManager.TAG));
142   }
143
144   public void test_filter_by_branch() throws Exception {
145     List<String> hashes = generateHistoryForFilters(true);
146     VcsLogBranchFilter branchFilter = VcsLogBranchFilterImpl.fromBranch("feature");
147     List<String> actualHashes = getFilteredHashes(branchFilter, null);
148     assertEquals(hashes, actualHashes);
149   }
150
151   public void test_filter_by_branch_and_user() throws Exception {
152     List<String> hashes = generateHistoryForFilters(false);
153     VcsLogBranchFilter branchFilter = VcsLogBranchFilterImpl.fromBranch("feature");
154     VcsLogUserFilter userFilter = new VcsLogUserFilterImpl(singleton(GitTestUtil.USER_NAME), Collections.<VirtualFile, VcsUser>emptyMap(),
155                                                            Collections.<VcsUser>emptySet());
156     List<String> actualHashes = getFilteredHashes(branchFilter, userFilter);
157     assertEquals(hashes, actualHashes);
158   }
159
160   public void test_short_details() throws Exception {
161     prepareLongHistory(VcsFileUtil.FILE_PATH_LIMIT * 2 / 40);
162     List<VcsCommitMetadata> log = log();
163
164     final List<String> hashes = ContainerUtil.newArrayList();
165     myLogProvider.readAllHashes(myProjectRoot, timedVcsCommit -> hashes.add(timedVcsCommit.getId().asString()));
166
167     List<? extends VcsShortCommitDetails> shortDetails = myLogProvider.readShortDetails(myProjectRoot, hashes);
168
169     Function<VcsShortCommitDetails, String> shortDetailsToString = getShortDetailsToString();
170     assertOrderedEquals(ContainerUtil.map(shortDetails, shortDetailsToString), ContainerUtil.map(log, shortDetailsToString));
171   }
172
173   public void test_full_details() throws Exception {
174     prepareLongHistory(VcsFileUtil.FILE_PATH_LIMIT * 2 / 40);
175     List<VcsCommitMetadata> log = log();
176
177     final List<String> hashes = ContainerUtil.newArrayList();
178     myLogProvider.readAllHashes(myProjectRoot, timedVcsCommit -> hashes.add(timedVcsCommit.getId().asString()));
179
180     List<? extends VcsFullCommitDetails> fullDetails = myLogProvider.readFullDetails(myProjectRoot, hashes);
181
182     // we do not check for changes here
183     final Function<VcsShortCommitDetails, String> shortDetailsToString = getShortDetailsToString();
184     Function<VcsCommitMetadata, String> metadataToString = details -> shortDetailsToString.fun(details) + "\n" + details.getFullMessage();
185     assertOrderedEquals(ContainerUtil.map(fullDetails, metadataToString), ContainerUtil.map(log, metadataToString));
186   }
187
188   @NotNull
189   private Function<VcsShortCommitDetails, String> getShortDetailsToString() {
190     return details -> {
191       String result = "";
192
193       result += details.getId().toShortString() + "\n";
194       result += details.getAuthorTime() + "\n";
195       result += details.getAuthor() + "\n";
196       result += details.getCommitTime() + "\n";
197       result += details.getCommitter() + "\n";
198       result += details.getSubject();
199
200       return result;
201     };
202   }
203
204   /**
205    * Generates some history with two branches: master and feature, and made by two users.
206    * Returns hashes of this history filtered by the given parameters:
207    * @param takeAllUsers     if true, don't filter by users, otherwise filter by default user.
208    */
209   private List<String> generateHistoryForFilters(boolean takeAllUsers) {
210     List<String> hashes = ContainerUtil.newArrayList();
211     hashes.add(last());
212
213     GitTestUtil.setupUsername("bob.smith", "bob.smith@example.com");
214     if (takeAllUsers) {
215       String commitByBob = tac("file.txt");
216       hashes.add(commitByBob);
217     }
218     GitTestUtil.setupDefaultUsername();
219
220     hashes.add(tac("file1.txt"));
221     git("checkout -b feature");
222     String commitOnlyInFeature = tac("file2.txt");
223     hashes.add(commitOnlyInFeature);
224     git("checkout master");
225     String commitOnlyInMaster = tac("master.txt");
226
227     Collections.reverse(hashes);
228     refresh();
229     return hashes;
230   }
231
232   @NotNull
233   private List<String> getFilteredHashes(@Nullable VcsLogBranchFilter branchFilter,
234                                          @Nullable VcsLogUserFilter userFilter) throws VcsException {
235     VcsLogFilterCollectionImpl filters = new VcsLogFilterCollectionImpl(branchFilter, userFilter, null, null, null, null, null);
236     List<TimedVcsCommit> commits = myLogProvider.getCommitsMatchingFilter(myProjectRoot, filters, -1);
237     return ContainerUtil.map(commits, commit -> commit.getId().asString());
238   }
239
240   private static void prepareSomeHistory() {
241     tac("a.txt");
242     git("tag ATAG");
243     tac("b.txt");
244   }
245
246   private static void prepareLongHistory(int size) throws IOException {
247     for (int i = 0; i < size; i++) {
248       String file = "a" + (i % 10) + ".txt";
249       if (i < 10) {
250         tac(file);
251       }
252       else {
253         modify(file);
254       }
255     }
256   }
257
258   private static void createTaggedBranch() {
259     String hash = last();
260     tac("c.txt");
261     tac("d.txt");
262     tac("e.txt");
263     git("tag poor-tag");
264     git("reset --hard " + hash);
265   }
266
267   @NotNull
268   private static VcsUser getDefaultUser() {
269     return new VcsUserImpl(GitTestUtil.USER_NAME, GitTestUtil.USER_EMAIL);
270   }
271
272   @NotNull
273   private List<VcsCommitMetadata> log() {
274     String output = git("log --all --date-order --full-history --sparse --pretty='%H|%P|%ct|%s|%B'");
275     final VcsUser defaultUser = getDefaultUser();
276     final Function<String, Hash> TO_HASH = s -> HashImpl.build(s);
277     return ContainerUtil.map(StringUtil.splitByLines(output), record -> {
278       String[] items = ArrayUtil.toStringArray(StringUtil.split(record, "|", true, false));
279       long time = Long.valueOf(items[2]) * 1000;
280       return new VcsCommitMetadataImpl(TO_HASH.fun(items[0]), ContainerUtil.map(items[1].split(" "), TO_HASH), time,
281                                        myProjectRoot, items[3], defaultUser, items[4], defaultUser, time);
282     });
283   }
284 }